﻿// <copyright company="Antix Software Limited">
// 	Copyright (c) 2011 Antix Software Limited, Anthony Johnston
// </copyright>
// 
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.Mvc;
using System.Web.Routing;
using System.Collections.Generic;

namespace Sandbox.AddressBook.Web.Client.Application.Mvc {
    /// <summary>
    ///   <para>If Modified Attribute</para>
    /// </summary>
    public class IfModifiedAttribute : ActionFilterAttribute {
        /// <summary>
        ///   <para>Cache the action output, dependent on the passed url and parameters</para>
        /// </summary>
        /// <param name="dependencyKey">Key for the cache item</param>
        /// <param name = "dependencyParameterNames">Names of the parameters to url by</param>
        public IfModifiedAttribute(string dependencyKey, params string[] dependencyParameterNames) {
            _dependencyKeyPrefix = dependencyKey;
            _dependencyKeyParams = dependencyParameterNames;
        }

        #region properties

        private readonly string _dependencyKeyPrefix;
        private readonly string[] _dependencyKeyParams;

        [Serializable]
        public class CacheItem {
            public DateTime ModifiedOn { get; set; }
            public byte[] Data { get; set; }
        }

        #endregion

        public override void OnActionExecuting(
            ActionExecutingContext filterContext) {
            if (filterContext.HttpContext.Request.Url == null) return;

            // page is cached by full path
            var url = filterContext.HttpContext.Request.Url.PathAndQuery;
            var response = filterContext.HttpContext.Response;
            

            var requestETag = filterContext.HttpContext.Request.Headers["If-None-Match"];

            if (requestETag != null)
            {
                // not modified
                response.StatusCode = 304;
                response.StatusDescription = "Not Modified";
                response.AddHeader("Content-Length", "0");

                filterContext.Result = new EmptyResult();
            }

            var dependencyKey = IfModifiedUtil.GetDependencyKey(
                            _dependencyKeyPrefix,
                            _dependencyKeyParams.Select(n => filterContext.ActionParameters[n]));

            var cacheETag = string.Format("{0:u}{1}", DateTime.Now.AddYears(1), url);

            var setStandardHeaders
                = (Action<string>)(eTag =>
                {
                    response.Cache.SetCacheability(HttpCacheability.Public);
                    response.Cache.SetExpires(DateTime.Now.AddYears(1));
                    response.Cache.SetETag(eTag);
                });

            setStandardHeaders(cacheETag);
        }        
    }

    public static class IfModifiedUtil {

        public static void Modified(
            this Cache cache,
            string prefix, params object[] parameterValues) {

            cache.Remove(GetDependencyKey(prefix, parameterValues));
        }

        internal static string GetDependencyKey(
            string prefix, IEnumerable<object> parameterValues) {

                return prefix
                    + string.Join("_",
                        parameterValues.Select(v => v == null ? "" : v.ToString()).ToArray());
        }
    }
}